﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Popup.cs" company="Marcin Jastrzebski">
//   Copyright (c) Marcin Jastrzebski (marcin@marcinj.net)
// </copyright>
// <summary>
//   Represents a popup.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace PopupLibrary
{
    using System;
    using System.Threading.Tasks;

    using Windows.Foundation;
    using Windows.UI.Core;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Input;

    /// <summary>Represents a popup.</summary>
    public class Popup
    {
        /// <summary>The overlay.</summary>
        private readonly Border overlay;

        /// <summary>The UI popup.</summary>
        private readonly Windows.UI.Xaml.Controls.Primitives.Popup popup;

        /// <summary>The completion source.</summary>
        private readonly TaskCompletionSource<object> completionSource;

        /// <summary>Initializes a new instance of the <see cref="Popup"/> class.</summary>
        /// <param name="element">The element.</param>
        /// <param name="settings">The settings.</param>
        internal Popup(UIElement element, PopupSettings settings)
        {
            this.completionSource = new TaskCompletionSource<object>();

            this.overlay = new Border
            {
                Background = settings.OverlayBackground
            };

            if (settings.CloseOnOverlayTap)
            {
                // user wants to close popup when overlay is tapped
                this.overlay.Tapped += this.OnOverlayTapped;
                this.overlay.IsTapEnabled = true;
            }

            this.popup = new Windows.UI.Xaml.Controls.Primitives.Popup
            {
                HorizontalAlignment = HorizontalAlignment.Stretch,
                VerticalAlignment = VerticalAlignment.Stretch,
                Child = this.overlay
            };

            this.overlay.Child = element;

            Window.Current.SizeChanged += this.OnWindowSizeChanged;
            this.UpdateSize();
        }

        /// <summary>The close async.</summary>
        /// <param name="result">The result.</param>
        /// <returns>The task tracking completion.</returns>
        public async Task CloseAsync(object result)
        {
            await this.popup.Dispatcher.RunAsync(
                CoreDispatcherPriority.Normal,
                () =>
                    {
                        this.popup.IsOpen = false;
                        this.completionSource.SetResult(result);
                    }).AsTask();
        }

        /// <summary>Shows the popup. Returns when the popup is closed.</summary>
        /// <returns>The popup result.</returns>
        internal async Task<object> ShowAsync()
        {
            this.popup.IsOpen = true;
            return await this.completionSource.Task;
        }

        /// <summary>Called when window size changed.</summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event arguments.</param>
        private void OnWindowSizeChanged(object sender, WindowSizeChangedEventArgs e)
        {
            this.UpdateSize();
        }

        /// <summary>Called when the overlay is tapped (if enabled).</summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event arguments.</param>
        private async void OnOverlayTapped(object sender, TappedRoutedEventArgs e)
        {
            // was the overlay tapped?
            if (ReferenceEquals(e.OriginalSource, this.overlay))
            {
                // yes, close the popup
                await this.CloseAsync(null);
            }
        }

        /// <summary>Updates the size of the overlay.</summary>
        private void UpdateSize()
        {
            Rect bounds = Window.Current.CoreWindow.Bounds;
            this.popup.HorizontalOffset = bounds.Left;
            this.popup.VerticalOffset = bounds.Top;
            this.overlay.Height = bounds.Height;
            this.overlay.Width = bounds.Width;
        }
    }
}